/**
 *	CSCI 3033 Final Project
 *
 *	@author:  	Cody Hazelwood, Micah High
 *  @version:	1.0
 *	Due Date:	May 5, 2010
 *	Platform:	Android 2.1-update 1
 *				Android API version 7
 *	Comments:	This is the Recording View.  It allows
 *              making a recording and playing it back.
 *
 **/

// TODO Markers need to be reimplemented to allow serializing of them inside the Recording object

package csci3033.RecordaNote;

import android.app.Activity;
//import android.content.Context;
import android.media.MediaPlayer;
//import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaRecorder;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
//import android.os.PowerManager;
import android.view.View;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.Toast;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.Runnable;
import java.util.ArrayList;
import java.util.Calendar;


public class RecordingScreen extends Activity implements MediaPlayer.OnCompletionListener, SeekBar.OnSeekBarChangeListener, Runnable {
	private String        filename;	//Filename to store serialized Marker Objects
	private Button        btnNewMarker;             //Button to make a New Marker
	private ImageButton   btnRecord;
	private ImageButton   btnStop;
	private ImageButton   btnPlay;
	private ProgressBar   seekBar1;                 //Progress/Seek Bar for audio playback
	private TextView      counter;
	private ListView      lstMarkersList;           //List View to list Marker Objects
	private MediaRecorder record;
	private MediaPlayer   play;
	private boolean       currentlyRecording = false;
	
	private Handler counterHandler = new Handler();  //GUI Thread Handler for counter display updating
	
	// TODO Implement Screen Locks during recording and playback
	//PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
	//PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, "My Tag");
	
	private String fileDirectory = Environment.getExternalStorageDirectory().getAbsolutePath();
	private String FILENAME = null;
	private String stamp;
	private ArrayList<Marker> markers = new ArrayList<Marker>();
	
	private long markerTime;
	private long startTime; 
	
    public void onCreate(Bundle savedInstanceState) {
    	super.onCreate(savedInstanceState);
		
    	setContentView(R.layout.recording);
		initControls();

		if(currentlyRecording)
			restoreMarkersList();
		lstMarkersList.setAdapter(new MarkerAdapter(this, R.layout.marker_row, markers));
		
		btnNewMarker.setOnClickListener(new Button.OnClickListener(){
			public void onClick(View v) {
				createMarker();
			}
		});
		
		btnRecord.setOnClickListener(new ImageButton.OnClickListener(){
			public void onClick(View v) {
				startRecording();
			}
		});
		
		btnStop.setOnClickListener(new ImageButton.OnClickListener(){
			public void onClick(View v) {
				stopRecording();
			}
		});
		
		btnPlay.setOnClickListener(new ImageButton.OnClickListener(){
			public void onClick(View v) {
				playRecording();
			}
		});
		
		//Allow only valid options to be clicked
		btnStop.setEnabled(false);
		btnRecord.setEnabled(true);
		if (FILENAME == null) {
			btnPlay.setEnabled(false);
		}
		
    } // end of onCreate()
    
     private void initControls() {
    	btnNewMarker = (Button) findViewById(R.id.btnNewMarker);
    	btnRecord = (ImageButton) findViewById(R.id.btnRecord);
    	btnStop = (ImageButton) findViewById(R.id.btnStop);
    	btnPlay = (ImageButton) findViewById(R.id.btnPlay);
    	lstMarkersList = (ListView) findViewById(R.id.lstMarkersList);
    	seekBar1 = (ProgressBar) findViewById(R.id.seekBar1);
    	counter = (TextView) findViewById(R.id.counter);
    }// end of initControls()
    
     /**
      * Reload the list of markers
      */
    private void restoreMarkersList() {
    	File					file     = new File (getExternalFilesDir(null), filename);
    	FileInputStream 		inFile   = null;
    	ObjectInputStream 		inObject = null;
    	
    	try {
    		if(file != null) {
    			inFile   = new FileInputStream(file);
    			inObject = new ObjectInputStream(inFile);
    			
    			int size = inObject.readInt();
    			for (int i=0; i < size; i++) {
    				markers.add((Marker)inObject.readObject());
    			}
    			
    			inObject.close();
    			inFile.close();
    		}
    	}
    	catch (IOException exception) {
    		Toast.makeText(getApplicationContext(), "Error!\n" + exception.getMessage(), 5).show();
    	}
    	catch (ClassNotFoundException exception) {
    		Toast.makeText(getApplicationContext(), "Class not found!\n" + exception.getMessage(), 5).show();
    	}
    }
    
    /**
     * Save the list of markers.
     */
    private void saveApplicationData() {
    	File					file      = new File (getExternalFilesDir(null), filename);
    	FileOutputStream 		outFile   = null;
    	ObjectOutputStream 		outObject = null;
    	try {
    		outFile   = new FileOutputStream(file);
    		outObject = new ObjectOutputStream(outFile);
    		
    		if (markers.size() > 0) {
    			outObject.writeInt(markers.size());
    			for (int i=0; i < markers.size(); i++){
    				outObject.writeObject(markers.get(i));
    			}
    		}
    		
    		outObject.close();
    		outFile.close();
    	}
    	catch (IOException exception) {
    		Toast.makeText(getApplicationContext(), "Error!\n" + exception.getMessage() + "I'm a save error.", 25).show();
    	}
    }
    
    /**
     * Creates a marker
     */
    private void createMarker() {
    	markerTime=System.currentTimeMillis()- startTime;
    	
    	if (currentlyRecording) {
    		saveApplicationData();
        	markers.add( new Marker(markerTime , markers.size(), currentlyRecording));
    		lstMarkersList.setAdapter(new MarkerAdapter(this, R.layout.marker_row, markers));
    	}
    	else {
    		Toast.makeText(getApplicationContext(), "Markers can only be\n"+"made while recording!", 25).show();
    	}
    }
    
    /**
     * Starts a recording.
     */
    private void startRecording() {
    	boolean ready = false;     //Ready to start recording
    	currentlyRecording = true;
        
    	//Acquire Screen WakeLock
    	//wl.acquire();
    	
    	//Generate Filename in the format "/rec_YYYY.MM.DD_HH.MM.mp4"
    	Calendar now = Calendar.getInstance();
    	stamp = now.get(Calendar.YEAR) + "."
    	      + now.get(Calendar.MONTH) + "."
    	      + now.get(Calendar.DAY_OF_MONTH) + "_"
    	      + now.get(Calendar.HOUR_OF_DAY) + "."
    	      + now.get(Calendar.MINUTE);
    	FILENAME = fileDirectory + "/rec_" + stamp + ".mp4";

    	//Generate maker's filename - marker_YYY.MMM.DD_HH.MM.sod
    	filename = "marker_"+stamp+".sod";

       	//Setup Recording
    	record = new MediaRecorder();                              //Create MediaRecorder Instance
    	record.setAudioSource(MediaRecorder.AudioSource.MIC);      //Set the Audio Source to Device MIC (or line in)
    	record.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); //Set the Format to MP4
    	record.setOutputFile(FILENAME);                            //Set the Filename
    	record.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); //Set the Audio Encoder
    	
    	//Prepare Recording
    	try {
			record.prepare();
			ready = true;
		} catch (IllegalStateException e) {
			//wl.release();  //Release Screen Lock
			Toast.makeText(getApplicationContext(), "Error!\n" + e.getMessage(), 25).show();
		} catch (IOException e) {
			//wl.release();  //Release Screen Lock
			Toast.makeText(getApplicationContext(), "IO Error!\n" + e.getMessage(), 25).show();
		}
		
		// set startTime
		startTime=System.currentTimeMillis();
		
		//Begin Recording
		if (ready) {
			//Allow only valid buttons to be clicked after starting
			btnRecord.setEnabled(false);
    		btnStop.setEnabled(true);
    		btnPlay.setEnabled(false);
    	
    		currentlyRecording = true;
    		
    		record.start(); //Start Recording
    		//Start Chronometer for Recording
    		// TODO Implement Recording clock
    		long millis = System.currentTimeMillis() - startTime;
		    int seconds = (int) (millis / 1000);
		    int minutes = seconds / 60;
		    seconds     = seconds % 60;
	
		    counter.setText(String.format("%d:%02d.%02d", minutes, seconds, millis));

		}
    }
    
    /**
     * Stop recording
     */
    private void stopRecording() {
    	if (currentlyRecording) {
    	    record.stop();
    	    record.release();
    	    record = null;
    	    currentlyRecording = false;
    	}
    	else {
    	    play.release();
    		play = null;
    	}
    	btnRecord.setEnabled(true);
    	btnPlay.setEnabled(true);
		btnStop.setEnabled(false);
		
		//Release Screen Wake Lock
		//wl.release();
		
		//setResult(RESULT_OK);
    }
    
    /**
     * Play a recording
     */
    private void playRecording(){
    	//Acquire Screen Wake Lock
    	//wl.acquire();
    	
    	play = new MediaPlayer();
    	try {
    		play.setDataSource(FILENAME);
    		play.setOnCompletionListener(this);
    		
    		play.prepare();
    		play.start();
    		new Thread(this).start();
    		
    		((SeekBar) seekBar1).setOnSeekBarChangeListener(this);
    		seekBar1.setMax(play.getDuration());
    		
    	} catch (IOException e) {
    		//wl.release();
    		Toast.makeText(getApplicationContext(), "Error!\n" + e.getMessage(), 25).show();
    	}
    	
    	btnRecord.setEnabled(false);
    	btnPlay.setEnabled(false);
		btnStop.setEnabled(true);
    }
    
    /**
     * Allows playing a recording from a specific time (for marker playback)
     */
    private void playFrom(int time) {
    	//Acquire Screen Wake Lock
    	//wl.acquire();
    	
    	play = new MediaPlayer();
    	try {
    		play.setDataSource(FILENAME);
    		play.setOnCompletionListener(this);
    		
    		play.prepare();
    		play.start();
    		play.seekTo(time);
    		
    		new Thread(this).start();
    		
    		((SeekBar) seekBar1).setOnSeekBarChangeListener(this);
    		seekBar1.setMax(play.getDuration());
    		
    	} catch (IOException e) {
    		//wl.release();
    		Toast.makeText(getApplicationContext(), "Error!\n" + e.getMessage(), 25).show();
    	}
    	
    	btnRecord.setEnabled(false);
    	btnPlay.setEnabled(false);
		btnStop.setEnabled(true);
    }
    
    @Override
    public void onPause() {
        super.onPause();
        if (record != null) {
            record.release();
            record = null;
        }

        if (play != null) {
            play.release();
            play = null;
            
            counter.setText("0:00:00.00");
    		
    		btnRecord.setEnabled(true);
    		btnPlay.setEnabled(true);
    		btnStop.setEnabled(false);
        }
    }
    /**
     * Called when play back of a recording completes
     */
	public void onCompletion(MediaPlayer mp) {
		play.release();
		play = null;
		
		counter.setText("0:00:00.00");
		
		btnRecord.setEnabled(true);
		btnPlay.setEnabled(true);
		btnStop.setEnabled(false);
	}

	//When the SeekBar is adjusted, this is called to skip to that location
	public void onProgressChanged(SeekBar arg0, int arg1, boolean arg2) {
		if (arg2 && play.isPlaying()) {
			play.seekTo(arg1);
		}
	}

	public void onStartTrackingTouch(SeekBar arg0) {
		// TODO Auto-generated method stub
		//Not needed for this application
		
	}

	public void onStopTrackingTouch(SeekBar arg0) {
		// TODO Auto-generated method stub
		//Not needed for this application
	}
	/**
	 * This function updates the time
	 */
	public void updateCounter() {
		counterHandler.post(new Runnable() {
			public void run() {
				int hours, min, sec, ms;
				int calc = play.getCurrentPosition();
				hours = calc/3600000;
				calc  = calc - (3600000 * hours);
				min   = calc/60000;
				calc  = calc - (60000 * min);
				sec   = calc/1000;
				ms    = calc - (1000 * sec);
				// takes ms from 000 format to 00 format 
				ms = ms/10;
				
				//counter.setText(hours + ":" + min + ":" + sec + "." + ms);
				counter.setText(String.format("%d:%02d:%02d.%02d", hours, min, sec, ms));
			}
		});
	}

	/**
     * Function to update the seek bar during playback
     */
    public void run() {
    	 int currentPosition = 0;
         int total = play.getDuration();
         seekBar1.setMax(total);
         while (play != null && currentPosition < total) {
             try {
                 Thread.sleep(100);
                 currentPosition = play.getCurrentPosition();
                 seekBar1.setProgress(currentPosition);
                 updateCounter();
             } catch (InterruptedException e) {
                 return;
             } catch (Exception e) {
                 return;
             }
             seekBar1.setProgress(currentPosition);
         }
    }
}
